/**
 * XQuery to Scala Translator
 * Fatemeh Borran-Dejnabadi
 * 26 December 2004
 */
// $Id: Lib.scala 12834 2007-09-11 08:07:01Z emir $

package xquery2src

import java.lang.Double

import scala.xml._
import scala.collection.mutable._
import scala.collection.immutable._

object Lib extends Application {
  
  val pp = new PrettyPrinter(80, 5)

  def document(s: String): Elem = scala.xml.XML.load(s)

  def doc(s: String): Elem = scala.xml.XML.load(s)

  def attribute(n: Node, att: String): String = n match {
    case Text(s)       => ""
    //case CharData(s) => ""
    case _:SpecialNode => ""
    case _ =>
      val a = n.attribute(att)
      if (a.isEmpty || a.get.isEmpty) "" else a.get.toString
  }

  def attribute(nodes: NodeSeq, att: String): List[String] = {
    var l: List[String] = List()
    nodes.foreach(n => l = attribute(n,att)::l)
    l.reverse
  }

  def text(n: Node): String = n match {
    case Text(s)               => s
    //case CharData(s)         => s
    case Elem(_,_,_,_,Text(s)) => s
    case _                   => ""
  }

  def text(nodes: NodeSeq): List[String] = {
    var l: List[String] = List()
    nodes.foreach(n => l = text(n)::l);
    l.reverse
  }

  def number(s: String): double = Double.parseDouble(s)

  def position(nodes:NodeSeq, op:String, value:int):NodeSeq = op match {
    case "equal" =>
      var l:int = 0;
      for (val n <- nodes) yield {
	l = l + 1;
	if (l-1 < nodes.length && l-1 == value)
	  nodes.apply(l-1);
	else
	  Text("");
      }
    case "not_equal" =>
      var l:int = 0;
      for (val n <- nodes) yield {
	l = l + 1;
	if (l-1 < nodes.length && l-1 != value)
	  nodes.apply(l-1);
	else
	  Text("");
      }
    case "less_than" =>
      var l:int = 0;
      for (val n <- nodes) yield {
	l = l + 1;
	if (l-1 < nodes.length && l-1 < value)
	  nodes.apply(l-1);
	else
	  Text("");
      }
    case "less_equal" =>
      var l:int = 0;
      for (val n <- nodes) yield {
	l = l + 1;
	if (l-1 < nodes.length && l-1 <= value)
	  nodes.apply(l-1);
	else
	  Text("");
      }
    case "greater_than" =>
      var l:int = value+1;
      for (val n <- nodes) yield {
	l = l + 1;
	if (l-1 < nodes.length)
	  nodes.apply(l-1);
	else
	  Text("");
      }
    case "greater_equal" =>
      var l:int = value;
      for (val n <- nodes) yield {
	l = l + 1;
	if (l-1 < nodes.length)
	  nodes.apply(l-1);
	else
	  Text("");
      }
    case _ =>
      Console.println("not found operation!!!");
      nodes;
  }

  def predicate(nodes:NodeSeq, k:int):List[Node] = {
    for (val x <- List(1); nodes.length > k-1) yield {
      nodes.apply(k-1);
    }
  }

  def predicate(nodes:NodeSeq, node:String, s:String):NodeSeq = {
    for (val n <- (nodes \ node); n.child.exists(c => c.label.equals(s))) yield {
      n;
    }
  }

  def predicate(nodes:NodeSeq, node:String, k:int):NodeSeq = {
    for (val n <- nodes; (n \ node).length > k-1) yield {
      (n \ node).apply(k-1);
    }
  }

  def predicate(nodes:NodeSeq, node:String, op:String, elem:String, value:Any):NodeSeq = elem match {
    case "position" =>
      value match {
	case i:int =>
	  position(nodes \ node, op, i-1);
	case _ =>
	  Console.println("bad position function!!!");
	  nodes;
      }
    case _ =>
      op match {
	case "equal" =>
	  for (val n <- (nodes \ node); equal(n \ elem, value)) yield {
	    n;
	  }
	case "not_equal" =>
	  for (val n <- (nodes \ node); not_equal(n \ elem, value)) yield {
	    n;
	  }
	case "less_than" =>
	  for (val n <- (nodes \ node); less_than(n \ elem, value)) yield {
	    n;
	  }
	case "less_equal" =>
	  for (val n <- (nodes \ node); less_equal(n \ elem, value)) yield {
	    n;
	  }
	case "greater_than" =>
	  for (val n <- (nodes \ node); greater_than(n \ elem, value)) yield {
	    n;
	  }
	case "greater_equal" =>
	  for (val n <- (nodes \ node); greater_equal(n \ elem, value)) yield {
	    n;
	  }
	case _ =>
	  Console.println("not found operation!!!");
	  nodes;
      }
  }
 
  def equal(x:Any, y:Any):boolean = x match {
    case nodes:NodeSeq => 
      y match {
	case nodes2:NodeSeq =>
	  nodes.exists(n1 => nodes2.exists(n2 => text(n1).equals(text(n2))));
	case i:int =>
	  nodes.exists(n => Integer.parseInt(text(n)) == i);
	case s:String =>
	  nodes.exists(n => text(n).equals(s));
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
/*    case n:Node =>
      y match {
	case n2:Node =>
	  text(n).equals(text(n2));
	case s:String =>
	  text(n).equals(s);
	case i:int =>
	  Integer.parseInt(text(n)) == i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;	  
      }
 */   case s:String =>
      y match {
	case s2:String =>	
	  s.equals(s2);	
	case i:int =>
	  Integer.parseInt(s) == i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
    case i:int =>
      y match {
	case j:int =>
	  i == j;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
  }      

  def not_equal(x:Any, y:Any):boolean = x match {
    case nodes:NodeSeq => 
      y match {
	case nodes2:NodeSeq =>
	  nodes.exists(n1 => !nodes2.exists(n2 => text(n1).equals(text(n2))));
	case i:int =>
	  nodes.exists(n => Integer.parseInt(text(n)) != i);
	case s:String =>
	  nodes.exists(n => !text(n).equals(s));
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
/*    case n:Node =>
      y match {
	case n2:Node =>
	  !text(n).equals(text(n2));
	case s:String =>
	  !text(n).equals(s);
	case i:int =>
	  Integer.parseInt(text(n)) != i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;	  
      }
  */  case s:String =>
      y match {
	case s2:String =>	
	  !s.equals(s2);	
	case i:int =>
	  Integer.parseInt(s) != i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
    case i:int =>
      y match {
	case j:int =>
	  i != j;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
  }      

  def less_than(x:Any, y:Any):boolean = x match {
    case nodes:NodeSeq => 
      y match {
	case nodes2:NodeSeq =>
	  nodes.exists(n1 => nodes2.exists(n2 => text(n1).compareTo(text(n2)) < 0 ));
	case i:int =>
	  nodes.exists(n => Integer.parseInt(text(n)) < i);
	case s:String =>
	  nodes.exists(n => text(n).compareTo(s) < 0);
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
/*    case n:Node =>
      y match {
	case n2:Node =>
	  text(n).compareTo(text(n2)) < 0;
	case s:String =>
	  text(n).compareTo(s) < 0;
	case i:int =>
	  Integer.parseInt(text(n)) < i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;	  
      }
  */  case s:String =>
      y match {
	case s2:String =>	
	  s.compareTo(s2) < 0;	
	case i:int =>
	  Integer.parseInt(s) < i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
    case i:int =>
      y match {
	case j:int =>
	  i < j;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
  }

  def less_equal(x:Any, y:Any):boolean = x match {
    case nodes:NodeSeq => 
      y match {
	case nodes2:NodeSeq =>
	  nodes.exists(n1 => nodes2.exists(n2 => text(n1).compareTo(text(n2)) <= 0));
	case i:int =>
	  nodes.exists(n => Integer.parseInt(text(n)) <= i);
	case s:String =>
	  nodes.exists(n => text(n).compareTo(s) <= 0);
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
/*    case n:Node =>
      y match {
	case n2:Node =>
	  text(n).compareTo(text(n2)) <= 0;
	case s:String =>
	  text(n).compareTo(s) <= 0;
	case i:int =>
	  Integer.parseInt(text(n)) <= i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;	  
      }
 */   case s:String =>
      y match {
	case s2:String =>	
	  s.compareTo(s2) <= 0;	
	case i:int =>
	  Integer.parseInt(s) <= i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
    case i:int =>
      y match {
	case j:int =>
	  i <= j;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
  }

  def greater_than(x:Any, y:Any):boolean = x match {
    case nodes:NodeSeq => 
      y match {
	case nodes2:NodeSeq =>
	  nodes.exists(n1 => nodes2.exists(n2 => text(n1).compareTo(text(n2)) > 0));
	case i:int =>
	  nodes.exists(n => Integer.parseInt(text(n)) > i);
	case s:String =>
	  nodes.exists(n => text(n).compareTo(s) > 0);
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
/*    case n:Node =>
      y match {
	case n2:Node =>
	  text(n).compareTo(n2) > 0;
	case s:String =>
	  text(n).compareTo(s) > 0;
	case i:int =>
	  Integer.parseInt(text(n)) > i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;	  
      }
 */   case s:String =>
      y match {
	case s2:String =>	
	  s.compareTo(s2) > 0;	
	case i:int =>
	  Integer.parseInt(s) > i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
    case i:int =>
      y match {
	case j:int =>
	  i > j;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
  }

  def greater_equal(x:Any, y:Any):boolean = x match {
    case nodes:NodeSeq => 
      y match {
	case nodes2:NodeSeq =>
	  nodes.exists(n1 => nodes2.exists(n2 => text(n1).compareTo(text(n2)) >= 0));
	case i:int =>
	  nodes.exists(n => Integer.parseInt(text(n)) >= i);
	case s:String =>
	  nodes.exists(n => text(n).compareTo(s) >= 0);
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
/*    case n:Node =>
      y match {
	case n2:Node =>
	  text(n).compareTo(text(n2)) >= 0;
	case s:String =>
	  text(n).compareTo(s) >= 0;
	case i:int =>
	  Integer.parseInt(text(n)) >= i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;	  
      }
   */ case s:String =>
      y match {
	case s2:String =>	
	  s.compareTo(s2) >= 0;
	case i:int =>
	  Integer.parseInt(s) >= i;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
    case i:int =>
      y match {
	case j:int =>
	  i >= j;
	case _ =>
	  Console.println("bad equal function!!!");
	  false;
      }
  }

  def orderBy(nodes:List[Node], order:String):List[Node] = {
    if (order.equals("ascending")) {
      nodes.sort(less_than).reverse;
    }
    else {
      nodes.sort(less_than);
    }
  }

  def distinct_values(nodes:NodeSeq):List[Node] = {
    var set = new ListSet[Node];
    nodes.foreach(n => (set = set + n));
    set.toList.reverse;
  }

  def not(b:boolean):boolean = {
    !b;
  }

  def min(nodes:NodeSeq):Node = {
    var l:List[double] = List();
    nodes.foreach(n => l = number(text(n))::l);
    var min:double = l.head;
    l = l.tail;
    while (!l.isEmpty) {
      if (l.head < min)
	min = l.head;
      l = l.tail;
    }
    <min>{min}</min>;
  }

  def max(nodes:NodeSeq):Node = {
    var l:List[double] = List();
    nodes.foreach(n => l = number(text(n))::l);
    var max:double = l.head;
    l = l.tail;
    while (!l.isEmpty) {
      if (l.head >= max)
	max = l.head;
      l = l.tail;
    }
    <max>{max}</max>;
  }

  def count(nodes:NodeSeq):Node = {
    <count>{nodes.length}</count>;
  }

  def sum(nodes:NodeSeq):Node = {
    var s:double = 0.0;
    nodes.foreach(n => s = s + number(text(n)));
    <sum>{s}</sum>;
  }

  def avg(nodes:NodeSeq):Node = {
    var s:double = number(text(sum(nodes)));
    <avg>{s / nodes.length}</avg>;
  }

  def exists(nodes:NodeSeq):boolean = {
    (nodes.length != 0);
  }

  def empty(nodes:NodeSeq):boolean = {
    (nodes.length == 0);
  }

  def contains(text:String, s:String):boolean = {
    if (text.indexOf(s) == -1)
      false;
    else
      true;
  }

  def startsWith(text:String, s:String):boolean = {
    text.startsWith(s);
  }

  def endsWith(text:String, s:String):boolean = {
    text.endsWith(s);
  }

}
